/**
 * @file STL_Crypt_DES3.cpp
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.1
 * @date 2021-05-11
 * 
 * @copyright Copyright Sci-Tech Laboratories (c) 2021
 * 
 */

#include "STL_Crypt_DES3.h"
#include "STLOG.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "STL_Text.h"
#include "STL_Tools.h"
#include <string>
// #include "STL_Crypt_PKCS5Padding.h"

#include <openssl/err.h>
#include <openssl/des.h>
#include <openssl/evp.h>

namespace STL
{

    std::string printf_openssl_version()
    {
        stlog_info("%s", OPENSSL_VERSION_TEXT);
    }

    std::string encrypt_des_ede3_cbc_pkcs5(std::string data, std::string key, std::string iv)
    {

        std::string ret = "";
        // 处理KEY值
        unsigned char uc_key[24];
        memset(uc_key, 0, 24);
        memcpy(uc_key, key.c_str(), key.size() < 24 ? key.size() : 24);

        // 处理iv向量
        unsigned char uc_iv[8];
        memset(uc_iv, 0, 8);
        memcpy(uc_iv, iv.c_str(), iv.size() < 24 ? iv.size() : 24);

        // 处理数据
        std::string pkcs5_data = data; //PKCS5Padding(data); //EVP会自动补位
        size_t datalen = pkcs5_data.size();

        // 准备输入
        unsigned char *uc_data = (unsigned char *)malloc(datalen);
        memcpy(uc_data, pkcs5_data.c_str(), datalen);

        // 准备输出
        int resultlen = 0, tmplen = 0;
        unsigned char *uc_result = (unsigned char *)malloc(datalen);
        memset(uc_result, 0, datalen);

#if OPENSSL_VERSION_NUMBER > 0x1010000fL
        // 初始化上下文
        EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
        EVP_CIPHER_CTX_init(ctx);
        EVP_EncryptInit_ex(ctx, EVP_des_ede3_cbc(), NULL, uc_key, uc_iv);

        // 加密
        if (!EVP_EncryptUpdate(ctx, uc_result, &resultlen, uc_data, datalen))
        {
            printf("EVP_EncryptUpdate\n");
            return "";
        }
        // 最终
        if (!EVP_EncryptFinal_ex(ctx, uc_result + resultlen, &tmplen))
        {
            printf("EVP_EncryptFinal_ex\n");
            return "";
        }
        resultlen += tmplen;

        EVP_CIPHER_CTX_cleanup(ctx);
        EVP_CIPHER_CTX_free(ctx);
#else
        // 初始化上下文
        EVP_CIPHER_CTX ctx;
        EVP_CIPHER_CTX_init(&ctx);
        EVP_EncryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, uc_key, uc_iv);

        // 加密
        if (!EVP_EncryptUpdate(&ctx, uc_result, &resultlen, uc_data, datalen))
        {
            printf("EVP_EncryptUpdate\n");
            return "";
        }
        // 最终
        if (!EVP_EncryptFinal_ex(&ctx, uc_result + resultlen, &tmplen))
        {
            printf("EVP_EncryptFinal_ex\n");
            return "";
        }
        resultlen += tmplen;

        // 释放上下文
        EVP_CIPHER_CTX_cleanup(&ctx);
#endif
        // 获取数据
        ret = std::string((char *)uc_result, resultlen);
        STL_SAFE_FREE(uc_result);
        return ret;
    }
    std::string decrypt_des_ede3_cbc_pksc5(std::string data, std::string key, std::string iv)
    {
        // 处理KEY值
        unsigned char uc_key[24];
        memset(uc_key, 0, 24);
        memcpy(uc_key, key.c_str(), key.size() < 24 ? key.size() : 24);

        // 处理iv向量
        unsigned char uc_iv[8];
        memset(uc_iv, 0, 8);
        memcpy(uc_iv, iv.c_str(), iv.size() < 24 ? iv.size() : 24);

        // 处理数据
        size_t datalen = data.size();

        // 准备输入
        unsigned char *uc_data = (unsigned char *)malloc(datalen);
        memcpy(uc_data, data.c_str(), datalen);

        // 准备输出
        int resultlen = 0, tmplen = 0;
        unsigned char *uc_result = (unsigned char *)malloc(datalen);
        memset(uc_result, 0, datalen);

#if OPENSSL_VERSION_NUMBER > 0x1010000fL
        // 初始化上下文
        EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
        EVP_CIPHER_CTX_init(ctx);
        EVP_DecryptInit_ex(ctx, EVP_des_ede3_cbc(), NULL, uc_key, uc_iv);

        EVP_CIPHER_CTX_set_padding(ctx, true);

        // 加密
        if (!EVP_DecryptUpdate(ctx, uc_result, &resultlen, uc_data, datalen))
        {
            printf("EVP_DecryptUpdate Failed\n");
            return "";
        }
        // 最终
        if (!EVP_DecryptFinal_ex(ctx, uc_result + resultlen, &tmplen))
        {
            printf("EVP_DecryptFinal_ex Failed\n");
            return "";
        }
        resultlen += tmplen;

        EVP_CIPHER_CTX_cleanup(ctx);
        EVP_CIPHER_CTX_free(ctx);
#else
        //初始化上下文
        EVP_CIPHER_CTX ctx;
        EVP_CIPHER_CTX_init(&ctx);
        EVP_DecryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, uc_key, uc_iv);
        EVP_CIPHER_CTX_set_padding(&ctx, 1);

        if (!EVP_DecryptUpdate(&ctx, uc_result, &resultlen, uc_data, datalen))
        {
            printf("EVP_DecryptUpdate\n");
            return "";
        }

        // printf("uc_result1: %s\n", std::string((char *)uc_result, resultlen).c_str());
        // printf("uc_result2: %s\n", STL::STL_Text::toHex(std::string((char *)uc_result, resultlen)).c_str());

        if (!EVP_DecryptFinal_ex(&ctx, uc_result + resultlen, &tmplen))
        {
            printf("EVP_DecryptFinal_ex\n");
            return "";
        }
        resultlen += tmplen;

        // printf("uc_result3: %s\n", std::string((char *)uc_result, resultlen).c_str());
        // printf("uc_result4: %s\n", STL::STL_Text::toHex(std::string((char *)uc_result, resultlen)).c_str());

        // 释放上下文
        EVP_CIPHER_CTX_cleanup(&ctx);
#endif

        // 获取数据
        std::string ret = std::string((char *)uc_result, resultlen);
        STL_SAFE_FREE(uc_result);

        return ret;
    }

    /*
    int do_encrypt(const EVP_CIPHER *type, const unsigned char *intext, unsigned char *key, unsigned char *iv)
    {
        unsigned char outbuf[1024];
        int outlen, tmplen;

        EVP_CIPHER_CTX ctx;
        FILE *out;
        EVP_CIPHER_CTX_init(&ctx);
        EVP_EncryptInit_ex(&ctx, type, NULL, key, iv);

        // if (!EVP_EncryptUpdate(&ctx, outbuf, &outlen, intext, (int)strlen(intext)))
        if (!EVP_EncryptUpdate(&ctx, outbuf, &outlen, intext, 8))
        {
            printf("EVP_EncryptUpdate\n");
            return -1;
        }

        if (!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen))
        {
            printf("EVP_EncryptFinal_ex\n");
            return -1;
        }

        outlen += tmplen;
        EVP_CIPHER_CTX_cleanup(&ctx);

        for (int i = 0; i < 8; i++)
        {
            printf("%02x ", outbuf[i]);
        }
        printf("\n");
        return 0;

        out = fopen("cipher.bin", "wb");
        fwrite(outbuf, 1, outlen, out);
        fflush(out);
        fclose(out);

        return 0;
    }

    int do_decrypt(const EVP_CIPHER *type, unsigned char *key, unsigned char *iv)
    {
        unsigned char inbuf[1024] = {0};
        unsigned char outbuf[1024] = {0};
        int outlen, inlen, tmplen;

        EVP_CIPHER_CTX ctx;
        FILE *in = NULL;
        EVP_CIPHER_CTX_init(&ctx);
        EVP_DecryptInit_ex(&ctx, type, NULL, key, iv);
        //EVP_EncryptInit_ex(&ctx, type, NULL, des_key, des_iv);

        in = fopen("cipher.bin", "r");
        inlen = fread(inbuf, 1, sizeof(inbuf), in);
        fclose(in);

        printf("Readlen: %d\n", inlen);
        if (!EVP_DecryptUpdate(&ctx, outbuf, &outlen, inbuf, inlen))
        {
            printf("EVP_DecryptUpdate\n");
            return -1;
        }

        if (!EVP_DecryptFinal_ex(&ctx, outbuf + outlen, &tmplen))
        {
            printf("EVP_DecryptFinal_ex\n");
            return -1;
        }

        outlen += tmplen;
        EVP_CIPHER_CTX_cleanup(&ctx);

        printf("Result: \n%s\n", outbuf);

        return 0;
    }

    //加密 cbc zeropadding 自己实现
    std::string des_cbc_zero_encrypt(const std::string &clearText, const std::string &key)
    {
        static unsigned char cbc_iv[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
        //初始化IV向量
        std::string strCipherText;
        DES_cblock keyEncrypt, ivec;
        memset(keyEncrypt, 0, 8);

        if (key.length() <= 8)
            memcpy(keyEncrypt, key.c_str(), key.length());
        else
            memcpy(keyEncrypt, key.c_str(), 8);

        DES_key_schedule keySchedule;                     //密钥表
        DES_set_key_unchecked(&keyEncrypt, &keySchedule); //设置密钥,且不检测密钥奇偶性

        memcpy(ivec, cbc_iv, sizeof(cbc_iv));

        // 循环加密,每8字节一次
        const_DES_cblock inputText;
        DES_cblock outputText;
        std::vector<unsigned char> vecCiphertext;
        unsigned char tmp[8];

        for (int i = 0; i < clearText.length() / 8; i++)
        {
            memcpy(inputText, clearText.c_str() + i * 8, 8);
            DES_ncbc_encrypt(inputText, outputText, 8, &keySchedule, &ivec, DES_ENCRYPT); //加密
            memcpy(tmp, outputText, 8);

            for (int j = 0; j < 8; j++)
                vecCiphertext.push_back(tmp[j]);

            //重置ivec
            memcpy(ivec, outputText, 8);
        }

        if (clearText.length() % 8 != 0)
        {
            int tmp1 = clearText.length() / 8 * 8;
            int tmp2 = clearText.length() - tmp1;
            memset(inputText, 0, 8);
            memcpy(inputText, clearText.c_str() + tmp1, tmp2);
            // 加密函数
            DES_ncbc_encrypt(inputText, outputText, 8, &keySchedule, &ivec, DES_ENCRYPT); //加密
            memcpy(tmp, outputText, 8);

            for (int j = 0; j < 8; j++)
                vecCiphertext.push_back(tmp[j]);
        }

        strCipherText.clear();
        strCipherText.assign(vecCiphertext.begin(), vecCiphertext.end());
        return strCipherText;
    }

    std::string des_cbc_pkcs5_decrypt(const std::string &cipherText, const std::string &key)
    {
        static unsigned char cbc_iv[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
        //初始化IV向量
        std::string clearText;
        DES_cblock keyEncrypt, ivec;
        memset(keyEncrypt, 0, 8);

        if (key.length() <= 8)
            memcpy(keyEncrypt, key.c_str(), key.length());
        else
            memcpy(keyEncrypt, key.c_str(), 8);

        DES_key_schedule keySchedule;                     //密钥表
        DES_set_key_unchecked(&keyEncrypt, &keySchedule); //设置密钥,且不检测密钥奇偶性

        memcpy(ivec, cbc_iv, sizeof(cbc_iv));

        // 循环解密,每8字节一次
        const_DES_cblock inputText;
        DES_cblock outputText;
        std::vector<unsigned char> vecCleartext;
        unsigned char tmp[8];

        for (int i = 0; i < cipherText.length() / 8; i++)
        {
            memcpy(inputText, cipherText.c_str() + i * 8, 8);
            DES_ncbc_encrypt(inputText, outputText, 8, &keySchedule, &ivec, DES_DECRYPT); //解密
            memcpy(tmp, outputText, 8);

            for (int j = 0; j < 8; j++)
                vecCleartext.push_back(tmp[j]);

            //重置ivec
            //memcpy(ivec, outputText, 8);  //解密过程不需要用前一块的结果作为下一块的IV
        }

        if (clearText.length() % 8 != 0)
        {
            int tmp1 = clearText.length() / 8 * 8;
            int tmp2 = clearText.length() - tmp1;
            memset(inputText, 0, tmp2);
            memcpy(inputText, cipherText.c_str() + tmp1, tmp2);
            DES_ncbc_encrypt(inputText, outputText, tmp2, &keySchedule, &ivec, DES_DECRYPT); //解密
            memcpy(tmp, outputText, tmp2);
            for (int j = 0; j < 8; j++)
                vecCleartext.push_back(tmp[j]);
        }
        clearText.clear();
        clearText.assign(vecCleartext.begin(), vecCleartext.end());
        return clearText;
    }
    std::string des_cbc_pkcs5_encrypt(const std::string &clearText, const std::string &key)
    {
        static unsigned char cbc_iv[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
        //初始化IV向量
        std::string strCipherText;
        DES_cblock keyEncrypt, ivec;
        memset(keyEncrypt, 0, 24);

        if (key.length() <= 24)
            memcpy(keyEncrypt, key.c_str(), key.length());
        else
            memcpy(keyEncrypt, key.c_str(), 8);

        DES_key_schedule keySchedule;                     //密钥表
        DES_set_key_unchecked(&keyEncrypt, &keySchedule); //设置密钥,且不检测密钥奇偶性

        memcpy(ivec, cbc_iv, sizeof(cbc_iv));

        // 循环加密,每8字节一次
        const_DES_cblock inputText;
        DES_cblock outputText;
        std::vector<unsigned char> vecCiphertext;
        unsigned char tmp[8];

        for (int i = 0; i < clearText.length() / 8; i++)
        {
            memcpy(inputText, clearText.c_str() + i * 8, 8);
            DES_ncbc_encrypt(inputText, outputText, 8, &keySchedule, &ivec, DES_ENCRYPT); //加密
            memcpy(tmp, outputText, 8);

            for (int j = 0; j < 8; j++)
                vecCiphertext.push_back(tmp[j]);

            //重置ivec
            memcpy(ivec, outputText, 8);
        }

        if (clearText.length() % 8 != 0)
        {
            int tmp1 = clearText.length() / 8 * 8;
            int tmp2 = clearText.length() - tmp1;
            memset(inputText, (8 - tmp2), 8);
            memcpy(inputText, clearText.c_str() + tmp1, tmp2);
        }
        else
        {
            memset(inputText, 8, 8);
        }
        // 加密函数
        DES_ncbc_encrypt(inputText, outputText, 8, &keySchedule, &ivec, DES_ENCRYPT); //加密
        memcpy(tmp, outputText, 8);

        for (int j = 0; j < 8; j++)
            vecCiphertext.push_back(tmp[j]);

        strCipherText.clear();
        strCipherText.assign(vecCiphertext.begin(), vecCiphertext.end());
        return strCipherText;
    }

    std::string DecodeCBCDES3(const std::string &strKey, const std::string &strData, unsigned char *cIV)
    {
        // 处理密匙
        const char *keystring = strKey.c_str();
        DES_cblock key;
        DES_key_schedule key_schedule;
        DES_string_to_key(keystring, &key);
        if (DES_set_key_checked(&key, &key_schedule) != 0)
        {
            stlog_error("convert to key_schedule failed.");
            return "";
        }

        // 处理向量
        DES_cblock ivec;
        memcpy((char *)&ivec, cIV, 8);

        //解密
        size_t len = strData.size();
        unsigned char *output = (unsigned char *)malloc(len);
        DES_cbc_encrypt((unsigned char *)(strData.c_str()), output, len, &key_schedule, &ivec, DES_DECRYPT);
        std::string tempdata = std::string((char *)output, len);
        STL_SAFE_FREE(output);
        stlog_debug("%s", STL_Text::StringToHexString(tempdata).c_str());
        std::string ret = PKCS5UnPadding(tempdata);
        stlog_debug("%s", STL_Text::StringToHexString(ret).c_str());
        return ret;
    }
    //openssl enc
    std::string EncodeCBCDES3(const std::string &strKey, const std::string &strdata, unsigned char *cIV)
    {
        // 处理密匙
        const char *keystring = strKey.c_str();
        stlog_debug("KEY:%s", STL_Text::StringToHexString(strKey).c_str());
        DES_cblock key;
        DES_key_schedule key_schedule;
        DES_string_to_key(keystring, &key);
        if (DES_set_key_checked(&key, &key_schedule) != 0)
        {
            stlog_error("convert to key_schedule failed.");
            return "";
        }

        // 处理向量
        DES_cblock ivec;
        memcpy((char *)&ivec, cIV, 8);
        stlog_debug("IV:%s", STL_Text::StringToHexString(std::string((char *)cIV, 8)).c_str());

        // 处理数据
        stlog_debug("DATA1:%s", STL_Text::StringToHexString(strdata).c_str());
        std::string tempdata = PKCS5Padding(strdata);
        stlog_debug("DATA2:%s", STL_Text::StringToHexString(tempdata).c_str());

        //加密
        size_t len = tempdata.size();
        // unsigned char *output = (unsigned char *)malloc(len);
        const_DES_cblock inputText;
        DES_cblock outputText;
        memcpy(inputText, tempdata.c_str(), 8);
        DES_ncbc_encrypt(inputText, outputText, len, &key_schedule, &ivec, DES_ENCRYPT);
        std::string ret = std::string((char *)outputText, len);

        // 正确结果：julong -> 15 36 E6 B9 AA 01 16 72
        // STL_SAFE_FREE(output);
        stlog_debug("ENCODE:%s", STL_Text::StringToHexString(ret).c_str());

        // DecodeCBCDES3(strKey, ret, cIV);

        return ret;
    }
*/
}